/*
 * Pane.cpp
 *
 *  Created on: 2011-3-1
 *      Author: hao
 */

#include "Pane.h"
#include <iconv.h>
#include <cstring>
#include <error.h>

Pane * Pane::pSelf = NULL;

bool Pane::SetupFile(const char* path) {
    if (bSetup) return false;

    fileId = open(path, O_RDONLY);

    if (fileId == -1) {

        char buffer[255];

        if (!CodeConver("utf-8", "GBK", path, buffer, 255))
            return false;

        fileId = open(buffer, O_RDONLY);

        if (fileId == -1)
            return false;

    }

    fstat(fileId, &fileStat);

    pData = mmap(0, fileStat.st_size, PROT_READ, MAP_SHARED, fileId, 0);

    if (!pData) return false;

    bSetup = true;

    return true;

}

bool Pane::CodeConver(const char * toCharSet, const char * fromCharSet, const char * str, char * buffer, size_t bufferlen) {
    iconv_t cd;

    cd = iconv_open(toCharSet, fromCharSet);

    char input[255];

    memset(input, 0, 255);
    memcpy(input, str, strlen(str));
    memset(buffer, 0, bufferlen);

    char * pin = input;
    char * * pout = &buffer;

    size_t inputlen = strlen(str);
    size_t outputlen = bufferlen;

    if (iconv(cd, &pin, &inputlen, pout, &outputlen) == (size_t) - 1) {

        //int err = errno;

        //const char * errorText = strerror(err);

        return false;

    }

    buffer[ bufferlen - outputlen ] = '\0';

    iconv_close(cd);

    return true;
}

bool Pane::strRecoding(const char * str, char * buffer, size_t bufferlen) {

    if (CodeConver("utf-8", "GB18030", str, buffer, bufferlen))
        return true;


    return false;

}

bool Pane::UnsetFile() {
    if (!bSetup) return false;

    munmap(pData, fileStat.st_size);

    pData = NULL;

    close(fileId);

    fileId = -1;

    bSetup = false;

    return true;
}

void Pane::drainCallback() {

    pSelf->Pause();

    if (pSelf->playEndCallback) {
        pSelf->playEndCallback();
    }

}

Pane::Pane() : callback(&Master, &pinSlot), bSetup(false), EQPane( NULL ) {
    CheckPlugin();
    SetupPlugin();
    pSelf = this;
}

bool Pane::Seek(int min, int second, int frame) {
    if (!pTerminal) return false;

    return Master.Seek(callback.getDecoderIndex(), min, second, frame);

}

Pane::~Pane() {
    streamEndFactroy.Destroy();
    Master.clearDecoder();
    decoderFactroy.Destroy();
    streamOperationFactroy.Destroy();
    for( unsigned i = 0; i < vPluginPane.size(); i++ ){
        delete vPluginPane[i];
    }
    vPluginPane.clear();
    pSelf = NULL;
}

void Pane::PlayingFile(const char * szPath, int * pMin, int * pSeconds, int * pFrame) {
    if (!pTerminal) return;

    if (!SetupFile(szPath)) throw Error(ErrorTag::MUSIC_FILE_NOT_FOUND);

    unsigned format;
    unsigned channels;
    unsigned rate;

    int index;

    if ((index = Master.SetupDecodingFile(pData, fileStat.st_size, &channels, &rate, &format)) == -1) {
        throw Error(ErrorTag::UNKNOWN_FILE_TYPE);
    }

    if (pMin || pSeconds || pFrame) {

        int Min = 0, Seconds = 0, Frame = 0;

        Master.getDecoderingFileTotalTime(index, &Min, &Seconds, &Frame);

        if (pMin)
            *pMin = Min;

        if (pSeconds)
            *pSeconds = Seconds;

        if (pFrame)
            *pFrame = Frame;

    }

    callback.setDecoderIndex(index);
    callback.setStreamState( format, channels, rate );

    pTerminal->CreateStream(callback, drainCallback, format, channels, rate);

}

void Pane::PlayingTrack(const CueSheet & sheet, unsigned TrackIndex, int * pMin, int * pSeconds, int * pFrame) {
    if (!pTerminal) return;

    if (TrackIndex >= sheet.TrackList.size()) throw Error(ErrorTag::TRACK_TIME_ERROR);

    int pos;
    for (pos = sheet.patch.size() - 1; pos >= 0; pos--) {
        if (sheet.patch[pos] == '/') break;
    }
    char chbuffer[255];
    getcwd(chbuffer, 255);
    if (pos != -1) {

        string tmp = sheet.patch.substr(0, pos);

        if (chdir(tmp.c_str())) {
            char buffer[255];
            if (!this->strRecoding(tmp.c_str(), buffer, 255)) throw Error(ErrorTag::MUSIC_FILE_NOT_FOUND);
            if (chdir(tmp.c_str())) throw Error(ErrorTag::MUSIC_FILE_NOT_FOUND);
        }

    }

    if (!SetupFile(sheet.vFilename[sheet.TrackList[TrackIndex].fileIndex].c_str())) {

        char buffer[255];
        //if (!this->strRecoding(tmp.c_str(), buffer, 255)) throw Error(ErrorTag::MUSIC_FILE_NOT_FOUND);

        if (!this->strRecoding(sheet.vFilename[sheet.TrackList[TrackIndex].fileIndex].c_str(), buffer, 255)) throw Error(ErrorTag::MUSIC_FILE_NOT_FOUND);

        char * buffer2 = new char[500];

        strcpy(buffer2, buffer);

        if (!SetupFile(buffer2)) {
            delete [] buffer2;
            chdir(chbuffer);
            throw Error(ErrorTag::MUSIC_FILE_NOT_FOUND);
        }
        delete [] buffer2;
    }

    chdir(chbuffer);

    unsigned format;
    unsigned channels;
    unsigned rate;

    int index;

    if ((index = Master.SetupDecodingTrack(pData, fileStat.st_size, &channels, &rate, &format, sheet, TrackIndex)) == -1) {
        throw Error(ErrorTag::UNKNOWN_FILE_TYPE);
    }

    int Min = 0, Seconds = 0, Frame = 0;

    unsigned long long FrameTmp;

    //Calculating the total time
    if (TrackIndex != sheet.TrackList.size() - 1 && sheet.TrackList[TrackIndex].fileIndex == sheet.TrackList[TrackIndex+1].fileIndex) {

        if (sheet.TrackList[ TrackIndex + 1 ].SpaceFrame || sheet.TrackList[ TrackIndex + 1 ].SpaceMin || sheet.TrackList[ TrackIndex + 1 ].SpaceSecond) {

            FrameTmp = sheet.TrackList[ TrackIndex + 1 ].SpaceMin * 60 * 75 + sheet.TrackList[ TrackIndex + 1 ].SpaceSecond * 75 + sheet.TrackList[ TrackIndex + 1 ].SpaceFrame;

            FrameTmp -= sheet.TrackList[ TrackIndex ].Min * 60 * 75 + sheet.TrackList[ TrackIndex ].Second * 75 + sheet.TrackList[ TrackIndex ].Frame;


        } else {
            FrameTmp = sheet.TrackList[ TrackIndex + 1 ].Min * 60 * 75 + sheet.TrackList[ TrackIndex + 1 ].Second * 75 + sheet.TrackList[ TrackIndex + 1 ].Frame;

            FrameTmp -= sheet.TrackList[ TrackIndex ].Min * 60 * 75 + sheet.TrackList[ TrackIndex ].Second * 75 + sheet.TrackList[ TrackIndex ].Frame;

        }

    } else {
        Master.getDecoderingFileTotalTime(index, &Min, &Seconds, &Frame);

        FrameTmp = Min * 60 * 75 + Seconds * 75 + Frame;

        FrameTmp -= sheet.TrackList[ TrackIndex ].Min * 60 * 75 + sheet.TrackList[ TrackIndex ].Second * 75 + sheet.TrackList[ TrackIndex ].Frame;

    }

    Min = FrameTmp / (60 * 75);

    FrameTmp %= 60 * 75;

    Seconds = FrameTmp / 75;

    FrameTmp %= 75;

    Frame = FrameTmp;

    if (pMin)
        *pMin = Min;
    if (pSeconds)
        *pSeconds = Seconds;
    if (pFrame)
        *pFrame = Frame;

    callback.setDecoderIndex(index);
    callback.setStreamState( format, channels, rate );

    pTerminal->CreateStream(callback, drainCallback, format, channels, rate);

}

void Pane::setVolumeDB(double db) {

    if (!pTerminal) return;

    pTerminal->SetVolumeDB(db);

}

void Pane::setVolumeLinear(double linear) {

    if (!pTerminal) return;

    pTerminal->SetVolumeLinear(linear);
}

void Pane::setChannelVolumeDB(int channel, double db) {

    if (!pTerminal) return;

    pTerminal->ChannleSetVolumeDB(channel, db);
}

void Pane::setChannelVolumeLinear(int channel, double linear) {
    if (!pTerminal) return;
    pTerminal->ChannleSetVolumeLinear(channel, linear);
}

void Pane::Pause() {
    if (!pTerminal) return;
    pTerminal->StreamPause();

}

bool Pane::isSetup() {
    return bSetup;
}

void Pane::Resume() {
    if (!pTerminal) return;
    pTerminal->StreamStart();

}

void Pane::Stop() {
    if (pTerminal)
        pTerminal->DestroyStream();
    UnsetFile();

}

std::string Pane::getSurportPrefixName() {

    return Master.getSurportPrefixName();

}

CueSheet Pane::AnalysisCue(const char * szPath) {

    return cueparser.AnalysisCUE(szPath);

}

double Pane::getCurrentTime() {
    return pTerminal->getStreamCurrentTime() / 1000.0;
}

void Pane::CheckPlugin() {

    pluginPath.clear();

    DIR * dir = opendir("plugins");

    if (!dir)
        throw Error(ErrorTag::PLUGHIN_DATA_NOT_FOUND);

    struct dirent * pRent;

    string temp = "plugins/";

    while ((pRent = readdir(dir))) {

        if (pRent->d_name[0] == '.')
            continue;

        pluginPath.push_back(string(temp + pRent->d_name));

    }

    closedir(dir);

}

void Pane::SetupPlugin() {

    int tmp;
    
    PluginCtrlPane * PCP;

    for (unsigned i = 0; i < pluginPath.size(); i++) {

        //插件索引设置
        try {
            
            PCP = new PluginCtrlPane( pluginPath[i].c_str() );
            if( PCP->Init() ){
                vPluginPane.push_back( PCP );
                if( PCP->getType() == PluginDefaultType::EQ )
                    EQPane = PCP;
            }
            else{
                delete PCP;
                PCP = NULL;
            }
            
            if ((tmp = decoderFactroy.addDecoder(pluginPath[i].c_str())) != -1) {
                DecoderIndex.push_back(tmp);
                continue;
            }

            if ((tmp = streamEndFactroy.addStreamTerminal(pluginPath[i].c_str())) != -1) {
                StreamEndIndex.push_back(tmp);
                continue;
            }

            if ((tmp = streamOperationFactroy.addOperationPlugin(pluginPath[i].c_str())) != -1) {
                StreamOperationIndex.push_back(tmp);
                continue;
            }
        } catch (Error &ex) {
            continue;
        }

    }

}

bool Pane::createCallback(unsigned streamendIndex) {

    //CallbackChecking

    if (streamendIndex > StreamEndIndex.size()) {
        return false;
    }

    pTerminal = streamEndFactroy.getStreamTerminal(StreamEndIndex[streamendIndex]);

    for (unsigned i = 0; i < DecoderIndex.size(); i++) {
        Master.addDecoder(decoderFactroy.getDecoder(DecoderIndex[i]));
    }

    for (unsigned i = 0; i < StreamOperationIndex.size(); i++) {
        pinSlot.addOperationPlugin(streamOperationFactroy.getOperationPlugin(StreamOperationIndex[i]));
    }

    return true;

}
